//
//  NSObject+SX_BaseModelData.m
//  SX_DataModel
//
//  Created by Ganide on 2018/7/4.
//  Copyright © 2018年 Ganide. All rights reserved.
//

#import "NSObject+SX_BaseModelData.h"
#import <objc/runtime.h>
#include <string.h>
#import <objc/message.h>
#import "ObjKeyUserInfo.h"
#import <UIKit/UIKit.h>
@implementation NSObject (SX_BaseModelData)

+ (id)sxBaseModelDictionry:(NSDictionary *)obj{
    if (!obj) {
        return nil;
    };
    
    id temObj = [[self alloc] sxInitBaseModelDictionry:obj];
    return temObj;
}
- (id) sxInitBaseModelDictionry:(NSDictionary *)obj{
    if(!obj || ![obj isKindOfClass:[NSDictionary class]]) return nil;
    dispatch_semaphore_t semap = dispatch_semaphore_create(1);
    dispatch_semaphore_wait(semap, DISPATCH_TIME_FOREVER);
    Class suClass = self.class;
    while (suClass) {
        const char *metaName = class_getName(suClass);
        NSString *metaStrin = [NSString stringWithUTF8String:metaName];
       //判断 supClass 是不是属于基类
        if ([metaStrin isEqualToString:@"NSObject"]) {
            break;
        }
        [self totalKvc:self dict:obj class:suClass];

        suClass = [suClass superclass];
    }
    dispatch_semaphore_signal(semap);

    return self;
}

- (NSArray *) sxBaseModelClass:(Class)objClass dataArr:(NSArray *)dataArr{
    if (!dataArr || [dataArr isKindOfClass:[NSNull class]] || !dataArr.count) {
        return nil;
    }
    NSMutableArray *temArray = [NSMutableArray array];
    [dataArr enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([obj isKindOfClass:[NSDictionary class]]) {
           id temObj = [objClass sxBaseModelDictionry:obj];
            if (temObj) {
                [temArray addObject:temObj];
            }
        }
        
    }];
    return temArray;
}
+ (NSArray *) sxBaseModelDataArr:(NSArray *)dataArr{
   return [self sxBaseModelClass:self dataArr:dataArr];
}
- (id)sxInitBaseModelJson:(id)json{
   NSDictionary *dict = [self sx_dictionaryWithJSON:json];
    if (dict) {
        return [self sxInitBaseModelDictionry:dict];
    }
    return nil;
}
+ (id) sxBaseModelJson:(id)json{
   return [[self alloc] sxInitBaseModelJson:json];
}
- (void)totalKvc:(id)temObj dict:(NSDictionary *)obj class:(Class)class{
    unsigned int count = 0;
    objc_property_t *propertys = class_copyPropertyList(class, &count);
    unsigned int attCount = 0;
    for (int i = 0; i < count; i++) {
        objc_property_t property = propertys[i];
        
        const char *key = property_getName(property);
        NSString *nameKey = [NSString stringWithUTF8String:key];
       
        objc_property_attribute_t *auttributes = property_copyAttributeList(property, &attCount);
        
        if (!obj[nameKey]||[obj[nameKey] isKindOfClass:[NSNull class]]) {
            continue;
        }
        objc_property_attribute_t auttri =auttributes[0];
        //NSString *strin = [NSString stringWithUTF8String:auttri.value];
        
        ObjKeyUserInfo *info = [ObjKeyUserInfo setKeyInfo:auttri.value nameKey:nameKey];
        SEL setter = [temObj modelEcixt:info.name];
        if (!setter) {
            continue;
        }
        switch (info.keyType) {
            case keyTypeArray:
            case keyTypeDictionry:
            case keyTypeSet:
            case keyTypeString:
            case keyTypeURL:
            case keyTypeValue:
            case keyTypeNumber:
            case keyTypeData:{
                if ([obj[info.name] isKindOfClass:info.dataClass]) {
                    [self sxSetValue:obj[info.name] obj:temObj sel:setter];
                }
                continue;
            }
                break;
            case keyTypeObj:
            case keyTypeVoid:
            {
                [self sxSetValue:obj[info.name] obj:temObj sel:setter];
                continue;
            }
                 break;
            case keyTypeChar:
            case keyTypeUnchar:
            case keyTypeInt:
            case keyTypeShort:
            case keyTypeLong:
            case keyTypeLongLong:
            case keyTypeUnint:
            case keyTypeUnShort:
            case keyTypeUnLong:
            case keyTypeUnLongLong:
            case keyTypeFloat:
            case keyTypeDouble:
            case keyTypebit:
            case keyTypeBool:{
                double d = [obj[info.name] doubleValue];
                if (isnan(d) || isinf(d)) {
                    [temObj setValue:@(0) forKey:info.name];
                }else{
                    [temObj setValue:obj[info.name] forKey:info.name];
                }
               
               continue;
            }
               
                break;
            case keyTypeLongDouble:{//特别处理
                long double d = [obj[info.name] doubleValue];
                if (isnan(d) || isinf(d)) d = 0;
                ((void (*)(id, SEL, long double))(void *) objc_msgSend)(temObj, setter, (long double)d);
                 continue;
            }
               
                break;
            case keyTypeSelector:{
                [temObj setValue:obj[info.name] forKey:info.name];
                 continue;
            }
               
                break;
            case keyTypeStruct:
            case keyTypeUnion:
            case keyTypePointer:{
                //[temObj setValue:obj[info.name] forKey:info.name];
                continue;
            }
                break;
            case keyTypeRect:
            case keyTypeSize:
            case keyTypePoint:
            case keyTypeRange:
            {
                
                if ([obj[info.name] isKindOfClass:info.dataClass]) {
                    [temObj setValue:obj[info.name] forKey:info.name];
                }
                
                continue;
            }
                
                break;
            case keyTypeDate:
#warning 这个地方由于考虑到后台数据返回的格式 故暂不做处理 TODO
                [self sxSetValue:obj[info.name] obj:temObj sel:setter];
                continue;
                break;
            case keyTypeOtherObj:
            {
                const char *property_attr = property_getAttributes(property);
                if (property_attr[1] == '@') {
                    char * occurs1 =  strchr(property_attr, '@');
                    char * occurs2 =  strrchr(occurs1, '"');
                    char dest_str[40]= {0};
                    strncpy(dest_str, occurs1, occurs2 - occurs1);
                    char * realType = (char *)malloc(sizeof(char) * 50);
                    int i = 0, j = 0, len = (int)strlen(dest_str);
                    for (; i < len; i++) {
                        if ((dest_str[i] >= 'a' && dest_str[i] <= 'z') || (dest_str[i] >= 'A' && dest_str[i] <= 'Z')) {
                            realType[j++] = dest_str[i];
                        }
                    }
                    
                    Class objClass = objc_getClass(realType);
                    id newObj = [objClass sxBaseModelDictionry:obj[info.name]];
                    if (newObj) {
                        [self sxSetValue:newObj obj:temObj sel:setter];
                    }
                    
                    free(realType);
                }
            }
                continue;
                break;
            default:
                continue;
                break;
        }
       
    }
    free(propertys);
}

- (void)sxSetValue:(id)value obj:(id)obj sel:(SEL)setter{
    ((void(*)(id,SEL,id))objc_msgSend)(obj, setter,value);
}

- (SEL) modelEcixt:(NSString *)key{
    
    NSString *keyFirst = [key substringToIndex:1].uppercaseString;
    NSString *keyLast = [key substringFromIndex:1];
    NSString *setKey = [NSString stringWithFormat:@"set%@%@:",keyFirst,keyLast];
    SEL setter = NSSelectorFromString(setKey);
    if ([self respondsToSelector:setter]) {
        return setter;
    }
    return nil;
}
- (NSDictionary *)sx_dictionaryWithJSON:(id)json {
    if (!json || json == (id)kCFNull) return nil;
    NSDictionary *dic = nil;
    NSData *jsonData = nil;
    if ([json isKindOfClass:[NSDictionary class]]) {
        dic = json;
    } else if ([json isKindOfClass:[NSString class]]) {
        jsonData = [(NSString *)json dataUsingEncoding : NSUTF8StringEncoding];
    } else if ([json isKindOfClass:[NSData class]]) {
        jsonData = json;
    }
    if (jsonData) {
        dic = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:NULL];
        if (![dic isKindOfClass:[NSDictionary class]]) dic = nil;
    }
    return dic;
}

- (NSDictionary *)dictFromObj{
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    dispatch_semaphore_t semap = dispatch_semaphore_create(1);
    dispatch_semaphore_wait(semap, DISPATCH_TIME_FOREVER);
    Class suClass = self.class;
    while (suClass) {
        const char *metaName = class_getName(suClass);
        NSString *metaStrin = [NSString stringWithUTF8String:metaName];
        //判断 supClass 是不是属于基类
        if ([metaStrin isEqualToString:@"NSObject"]) {
            break;
        }
        //[self totalKvc:self dict:obj class:suClass];
        [self totalKvc:self dict:dict propertysClass:suClass];
        suClass = [suClass superclass];
    }
    dispatch_semaphore_signal(semap);
    return dict;
}
+ (NSDictionary *)dictFromNeedObj:(id)obj{
    return [obj dictFromObj];
}

- (void)totalKvc:(id)temObj dict:(NSMutableDictionary *)dict propertysClass:(Class)class {
    unsigned int count = 0;
    objc_property_t *propertys = class_copyPropertyList(class, &count);
    unsigned int attCount = 0;
    for (int i = 0; i < count; i++) {
        objc_property_t property = propertys[i];
        
        const char *key = property_getName(property);
        NSString *nameKey = [NSString stringWithUTF8String:key];
        
        objc_property_attribute_t *auttributes = property_copyAttributeList(property, &attCount);
        
        
        objc_property_attribute_t auttri =auttributes[0];
        ObjKeyUserInfo *info = [ObjKeyUserInfo setKeyInfo:auttri.value nameKey:nameKey];
        switch (info.keyType) {
            case keyTypeArray:
            {
                NSArray *objs = [temObj valueForKey:nameKey];
                dict[nameKey] = [self arrClassHandel:objs isSet:NO];
            }
                continue;
                break;
            case keyTypeSet:{
                NSArray *objs = [temObj valueForKey:nameKey];
                dict[nameKey] = [self arrClassHandel:objs isSet:YES];
            }
                continue;
                break;
            case keyTypeDictionry:{
                dict[nameKey] = [self dictClassHandel:[temObj valueForKey:nameKey]];
            }
                continue;
                break;
            case keyTypeVoid:
            case keyTypeString:
            case keyTypeURL:
            case keyTypeValue:
            case keyTypeNumber:
            case keyTypeData:
            case keyTypeObj:
                dict[nameKey] = [temObj valueForKey:nameKey];
                continue;
                break;
            case keyTypeChar:
            case keyTypeUnchar:
            case keyTypeInt:
            case keyTypeShort:
            case keyTypeLong:
            case keyTypeLongLong:
            case keyTypeUnint:
            case keyTypeUnShort:
            case keyTypeUnLong:
            case keyTypeUnLongLong:
            case keyTypeFloat:
            case keyTypeDouble:
            case keyTypeBool:
                NSLog(@"%@",[temObj valueForKey:info.name]);
                dict[nameKey] = [temObj valueForKey:info.name];
                continue;
                break;
            case keyTypeLongDouble:{//特别处理
                dict[nameKey] = [temObj valueForKey:info.name];
            }
                continue;
                break;
            case keyTypeStruct:
            case keyTypeUnion:
            case keyTypebit:
            case keyTypePointer:
                continue;
                break;
            case keyTypeSelector:
            case keyTypeRect:
            case keyTypePoint:
            case keyTypeSize:
            case keyTypeRange:
              dict[nameKey] = [temObj valueForKey:info.name];
                continue;
                break;
            case keyTypeDate:
                dict[nameKey] = [temObj valueForKey:info.name];
                continue;
                break;
            case keyTypeOtherObj:
            {
                const char *property_attr = property_getAttributes(property);
                if (property_attr[1] == '@') {
                    char * occurs1 =  strchr(property_attr, '@');
                    char * occurs2 =  strrchr(occurs1, '"');
                    char dest_str[40]= {0};
                    strncpy(dest_str, occurs1, occurs2 - occurs1);
                    char * realType = (char *)malloc(sizeof(char) * 50);
                    int i = 0, j = 0, len = (int)strlen(dest_str);
                    for (; i < len; i++) {
                        if ((dest_str[i] >= 'a' && dest_str[i] <= 'z') || (dest_str[i] >= 'A' && dest_str[i] <= 'Z')) {
                            realType[j++] = dest_str[i];
                        }
                    }
                    id subObj = [temObj valueForKey:nameKey];
                    NSDictionary *subDict = [subObj dictFromObj];
                    dict[nameKey] = subDict;
                    
                    free(realType);
                }
            }
                continue;
                break;
            default:
                continue;
                break;
        }
        
    }
    free(propertys);
}
- (id)arrClassHandel:(NSArray *)arrObj isSet:(BOOL)isSet{
    NSMutableArray *tempArr = [NSMutableArray array];
    NSMutableSet *set = [NSMutableSet set];
    NSString *baseType =  @"__NSCFNumber__NSCFConstantString__NSCFString__NSDate_NSZeroDataNSConcreteMutableDataNSTaggedPointerString";
    NSString *dictString = @"__NSDictionaryI__NSDictionaryM";
    NSString *arrString = @"__NSArrayI__NSArrayM";
    NSString *setString = @"__NSSetI__NSSetM";
    
    for (id obj in arrObj) {
        NSString *className = [NSString stringWithUTF8String:object_getClassName(obj)];
        if ([baseType containsString:className]) {
            isSet?[set addObject:obj]:[tempArr addObject:obj];
        }else if ([dictString containsString:className]){
            isSet?[set addObject:[self dictClassHandel:obj]]:[tempArr addObject:[self dictClassHandel:obj]];
        }else if ([arrString containsString:className]){
            [tempArr addObject:[self arrClassHandel:obj isSet:NO]];
        }else if ([setString containsString:className]){
            [set addObject:[self arrClassHandel:[((NSSet *)obj) allObjects] isSet:YES]];
        }else{
            [tempArr addObject:[obj dictFromObj]];
        }
        
    }
    
    return isSet?set : tempArr;
}

- (NSDictionary *)dictClassHandel:(NSDictionary *)dict{
    NSMutableDictionary *temDict = [NSMutableDictionary dictionary];
    NSString *baseType =  @"__NSCFNumber__NSCFConstantString__NSCFString__NSDate_NSZeroDataNSConcreteMutableDataNSTaggedPointerString";
    NSString *dictString = @"__NSDictionaryI__NSDictionaryM";
    NSString *arrString = @"__NSArrayI__NSArrayM";
    NSString *setString = @"__NSSetI__NSSetM";
    for (id key in dict.allKeys) {
        NSString *className = [NSString stringWithUTF8String:object_getClassName(dict[key])];
        if ([baseType containsString:className]) {
            temDict[key] = dict[key];
        }else if ([dictString containsString:className]){
            temDict[key] = [self dictClassHandel:dict[key]];
        }else if ([arrString containsString:className]){
            temDict[key] = [self arrClassHandel:dict[key] isSet:NO];
        }else if ([setString containsString:className]){
            temDict[key] = [self arrClassHandel:[((NSSet *)dict[key]) allObjects] isSet:YES];
        }else{
            temDict[key] = [dict[key] dictFromObj];
        }
    }
    return temDict;
}
- (NSArray *)arrayFromArrObj:(NSArray *)objArray{
    if ((![objArray isKindOfClass:[NSArray class]])||([objArray isKindOfClass:[NSArray class]] && objArray.count < 1)) {
        return nil;
    }
    return [self arrClassHandel:objArray isSet:NO];
}
- (NSDictionary *)dictFromdictObj:(NSDictionary *)dict{
    if (![dict isKindOfClass:[NSDictionary class]]) {
        return nil;
    }
    return [self dictClassHandel:dict];
}
- (id)valueForUndefinedKey:(NSString *)key{
    return nil;
}
@end
